/*! \file rpn_stack.c
 *  \brief Reverse Polish Notation (RPN) stack 
 *  \author Lluis Garrido
 *
 *  This file contains the funcion necessary to manipulate
 *  a rpn_stack structure. The functions listed here are
 *  used internally by ::eval_expr_shunting_yard and
 *  ::eval_expr_evaluation.
 */

#include "eval_expr.h"

/*! 
 *  \brief Allocates a rpn_stack structure 
 *
 *  \param[in] nitems   The maximum number of items the stack can hold. 
 *
 *  \return A pointer to the allocated rpn_stack structure. 
 *                         
 */

struct rpn_stack *rpn_stack_allocate(int nitems)
{
  int i;
  struct rpn_stack *r;

  assert(nitems > 0);

  r = (struct rpn_stack *) malloc(sizeof(struct rpn_stack));

  if (!r)
  {
    printf("ERROR: not enough memory.\n");
    exit(EXIT_FAILURE);
  }

  r->el = (struct rpn_stack_item *) malloc(sizeof(struct rpn_stack_item) * nitems);

  if (!r->el)
  {
    printf("ERROR: not enough memory.\n");
    exit(EXIT_FAILURE);
  }

  r->nitems = nitems;
  r->currel    = 0;

  for(i = 0; i < nitems; i++)
    r->el[i].type_item = RPN_TYPE_NONE;

  return r;
}

/*! 
 *  \brief Gets number of items of the rpn_stack structure 
 *
 *  \param[in] r   A pointer to an allocated rpn_stack structure. 
 *
 *  \return The maximum number of items the stack can hold. 
 *                         
 */

int rpn_stack_get_nitems(struct rpn_stack *r)
{
  assert(r != NULL);

  return r->nitems;
}

/*! 
 *  \brief Push a number (RPN_TYPE_NUMBER) onto the stack 
 *         structure.
 *
 *  \param[in] r   A pointer to an allocated rpn_stack structure. 
 *  \param[in] number The number to be pushed onto the stack.
 *
 */

void rpn_stack_insert_number(struct rpn_stack *r, double number)
{
  int i;

  i = r->currel;

  if (i >= r->nitems)
  {
    printf("ERROR: expression is too large\n");
    exit(EXIT_FAILURE);
  }

  r->el[i].type_item    = RPN_TYPE_NUMBER;
  r->el[i].value.number = number;

  r->currel++;
}

/*! 
 *  \brief Push an operator (RPN_TYPE_OPERATOR) onto the stack 
 *         structure.
 *
 *  \param[in] r           A pointer to an allocated rpn_stack structure. 
 *  \param[in] operator_id The operator identifier to be pushed onto the stack.
 *
 *  See also ::eval_expr_get_operator_id
 *
 */

void rpn_stack_insert_operator(struct rpn_stack *r, int operator_id)
{
  int i;

  i = r->currel;

  if (i >= r->nitems)
  {
    printf("ERROR: expression is too large\n");
    exit(EXIT_FAILURE);
  }

  r->el[i].type_item          = RPN_TYPE_OPERATOR;
  r->el[i].value.operator_id  = operator_id;

  r->currel++;
}

/*! 
 *  \brief Push a function (RPN_TYPE_FUNCTION) onto the stack 
 *         structure.
 *
 *  \param[in] r           A pointer to an allocated rpn_stack structure. 
 *  \param[in] function_id The function identifier to be pushed onto the stack.
 *
 *  See also ::list_functions_get_function_id
 *
 */

void rpn_stack_insert_function(struct rpn_stack *r, int function_id)
{
 int i;

  i = r->currel;

  if (i >= r->nitems)
  {
    printf("ERROR: expression is too large\n");
    exit(EXIT_FAILURE);
  }

  r->el[i].type_item          = RPN_TYPE_FUNCTION;
  r->el[i].value.function_id  = function_id;

  r->currel++;
}

/*! 
 *  \brief Push a variable (RPN_TYPE_VARIABLE) onto the stack 
 *         structure.
 *
 *  \param[in] r          A pointer to an allocated rpn_stack structure. 
 *  \param[in] variable_id The function identifier to be pushed onto the stack.
 *
 *  See also ::list_variables_get_variable_id
 *
 */

void rpn_stack_insert_variable(struct rpn_stack *r, int variable_id)
{
 int i;

  i = r->currel;

  if (i >= r->nitems)
  {
    printf("ERROR: expression is too large\n");
    exit(EXIT_FAILURE);
  }

  r->el[i].type_item          = RPN_TYPE_VARIABLE;
  r->el[i].value.variable_id  = variable_id;

  r->currel++;
}

/*! 
 *  \brief Push a left parenthesis (RPN_TYPE_LPARENTHESIS) onto the stack 
 *         structure.
 *
 *  \param[in] r          A pointer to an allocated rpn_stack structure. 
 *
 */

void rpn_stack_insert_lparenthesis(struct rpn_stack *r)
{
  int i;

  i = r->currel;

  if (i >= r->nitems)
  {
    printf("ERROR: expression is too large\n");
    exit(EXIT_FAILURE);
  }

  r->el[i].type_item = RPN_TYPE_LPARENTHESIS;

  r->currel++;
}

/*! 
 *  \brief Push a rpn_stack_item onto the stack 
 *         structure.
 *
 *  \param[in] r    A pointer to an allocated rpn_stack structure. 
 *  \param[in] el   A pointer to a rpn_stack_item structure to be pushed. 
 *
 */

void rpn_stack_insert_item(struct rpn_stack *r, struct rpn_stack_item *el)
{
  int i;

  i = r->currel;

  if (i >= r->nitems)
  {
    printf("ERROR: expression is too large\n");
    exit(EXIT_FAILURE);
  }

  memcpy(r->el + i, el, sizeof(struct rpn_stack_item)); 

  r->currel++;
}

/*! 
 *  \brief Get a pointer to an item stored in the stack.
 *
 *  Get a pointer to an item stored in the stack. Note that there
 *  is no pop operation, that is, the stack is not decreased.
 *
 *  \param[in] r      A pointer to an allocated rpn_stack structure. 
 *  \param[in] index  The index of the item to be obtained. 
 *
 *  \return A pointer to the element stored at index's position of
 *          the stack.
 *
 */

struct rpn_stack_item *rpn_stack_get_item(struct rpn_stack *r, int index)
{
  assert(index >= 0);
  assert(index <= r->nitems);

  return (r->el + index); // equivalent a &(r->el[index])
}

/*! 
 *  \brief Get a pointer to the item stored at the top of the stack. 
 *
 *  Get a pointer to the item stored at the top of the stack. Note that there
 *  is no pop operation, that is, the stack is not decreased.
 *
 *  \param[in] r      A pointer to an allocated rpn_stack structure. 
 *
 *  \return A pointer to the element stored at the top of the stack.
 *
 */

struct rpn_stack_item *rpn_stack_get_previous_item(struct rpn_stack *r)
{
  struct rpn_stack_item *ret;

  ret = rpn_stack_get_item(r, r->currel - 1);

  return ret;
}

/*! 
 *  \brief Get the type of an item stored in the stack. 
 *
 *  Get the type of an item stored in the stack. Note that there
 *  is no pop operation, that is, the stack is not decreased.
 *
 *  \param[in] r      A pointer to an allocated rpn_stack structure. 
 *  \param[in] index  The index of the item to be obtained. 
 *
 *  \return The type of element stored at index's position of 
 *          the stack. May be RPN_TYPE_NONE, RPN_TYPE_NUMBER,
 *          RPN_TYPE_FUNCTION, RPN_TYPE_VARIABLE, RPN_TYPE_OPERATOR or
 *	    RPN_TYPE_LPARENTHESIS. 
 *
 */

int rpn_stack_get_type_item(struct rpn_stack *r, int index)
{
  assert(index >= 0);
  assert(index <= r->nitems);

  return r->el[index].type_item;
}

/*! 
 *  \brief Get the type of the item stored at the top of the stack.
 *
 *  Get the type of an item stored at the top of the stack. Note that there
 *  is no pop operation, that is, the stack is not decreased.
 *
 *  \param[in] r      A pointer to an allocated rpn_stack structure. 
 **
 *  \return The type of element stored at the top of the stack. 
 *          May be RPN_TYPE_NONE, RPN_TYPE_NUMBER,
 *          RPN_TYPE_FUNCTION, RPN_TYPE_VARIABLE, RPN_TYPE_OPERATOR or
 *	    RPN_TYPE_LPARENTHESIS. 
 *
 */

int rpn_stack_get_previous_type_item(struct rpn_stack *r)
{
  int ret;

  ret = rpn_stack_get_type_item(r, r->currel - 1);

  return ret;
}

/*! 
 *  \brief Pop and insert (push) from two stacks. 
 *
 *  \param[in] dest     Destination stack.
 *  \param[in] orig     Source stack. 
 *
 *  This function pops the element at the top of orig and
 *  pushes it on top of dest.
 *
 */

void rpn_stack_pop_and_insert_one_item(struct rpn_stack *dest, struct rpn_stack *orig)
{
  int i_dest, i_orig;

  assert(orig->nitems > 0);

  i_dest = dest->currel;
  i_orig = orig->currel - 1;

  if (i_dest >= dest->nitems)
  {
    printf("ERROR: expression is to large\n");
    exit(EXIT_FAILURE);
  }

  memcpy(dest->el + i_dest, orig->el + i_orig, sizeof(struct rpn_stack_item));

  orig->el[i_orig].type_item = RPN_TYPE_NONE;

  dest->currel++;
  orig->currel--;
}

/*! 
 *  \brief Free a rpn_stack structure. 
 *
 *  \param[in] r     The rpn_stack structure to be freed.
 *
 */

void rpn_stack_free(struct rpn_stack *r)
{
  assert(r);
  assert(r->el);

  free(r->el);
  free(r);
}


